Skip to content

fix(android/aarch64): use libc::ucontext_t from Android NDK#5189

Open
StackOverflowExcept1on wants to merge 1 commit into
rust-lang:mainfrom
StackOverflowExcept1on:fix-android
Open

fix(android/aarch64): use libc::ucontext_t from Android NDK#5189
StackOverflowExcept1on wants to merge 1 commit into
rust-lang:mainfrom
StackOverflowExcept1on:fix-android

Conversation

@StackOverflowExcept1on

@StackOverflowExcept1on StackOverflowExcept1on commented Jun 21, 2026

Copy link
Copy Markdown

@rustbot

rustbot commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

Some changes occurred in an Android module

cc @maurer

pub uc_sigmask__c_anonymous_union: __c_anonymous_uc_sigmask,
/* The kernel adds extra padding after uc_sigmask to match
* glibc sigset_t on ARM64. */
__padding: Padding<[c_char; 128 - size_of::<crate::sigset_t>()]>,

@teor2345 teor2345 Jun 25, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this subtracting sigset_t (32 bit?), but the comment refers to ARM64?

This calculation seems weird for either 32-bit or 64-bit, and might need further comments:

  • the union is the size of sigset64_t (assuming it's bigger) but we subtract sigset_t (the 32-bit size) from the padding

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@teor2345 teor2345 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not an expert here, but I have coded a lot of C, and some parts of this seem weird without further context

View changes since this review

if #[cfg(feature = "extra_traits")] {
impl PartialEq for __c_anonymous_uc_sigmask {
fn eq(&self, other: &__c_anonymous_uc_sigmask) -> bool {
unsafe { self.uc_sigmask == other.uc_sigmask }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this sound and correct if the 64-bit sigmask was initialised in the union?

  • if the 64-bit sigmask is larger and has no padding, then the equality check misses some bytes
  • if the 32-bit sigmask is larger (unlikely), then we're comparing uninitialised bytes
  • if there's padding, then does it need to be ignored?

impl Eq for __c_anonymous_uc_sigmask {}
impl hash::Hash for __c_anonymous_uc_sigmask {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
unsafe { self.uc_sigmask.hash(state) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, not sure we can do this with unions, without some guarantee they are the same size.

@StackOverflowExcept1on StackOverflowExcept1on Jun 25, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code was simply borrowed from the ARM module:

cfg_if! {
if #[cfg(feature = "extra_traits")] {
impl PartialEq for __c_anonymous_uc_sigmask {
fn eq(&self, other: &__c_anonymous_uc_sigmask) -> bool {
unsafe { self.uc_sigmask == other.uc_sigmask }
}
}
impl Eq for __c_anonymous_uc_sigmask {}
impl hash::Hash for __c_anonymous_uc_sigmask {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
unsafe { self.uc_sigmask.hash(state) }
}
}
}
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x86_64 has same layout:

cfg_if! {
if #[cfg(feature = "extra_traits")] {
impl PartialEq for __c_anonymous_uc_sigmask {
fn eq(&self, other: &__c_anonymous_uc_sigmask) -> bool {
unsafe { self.uc_sigmask == other.uc_sigmask }
}
}
impl Eq for __c_anonymous_uc_sigmask {}
impl hash::Hash for __c_anonymous_uc_sigmask {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
unsafe { self.uc_sigmask.hash(state) }
}
}
}
}

@StackOverflowExcept1on

StackOverflowExcept1on commented Jun 25, 2026

Copy link
Copy Markdown
Author

@teor2345 I created test repository demonstrating that the offsets are correct (Android NDK C++). It also has passed CI.

#include <ucontext.h>

static_assert(offsetof(ucontext_t, uc_flags) == 0);
static_assert(offsetof(ucontext_t, uc_link) == 8);
static_assert(offsetof(ucontext_t, uc_stack) == 16);
static_assert(offsetof(ucontext_t, uc_sigmask) == 40);
static_assert(offsetof(ucontext_t, uc_mcontext) == 176);

static_assert(sizeof(ucontext_t) == 4560);
git clone https://github.com/StackOverflowExcept1on/library-android.git
cd library-android
PATH_TO_NDK=~/Android/Sdk/ndk/27.2.12479018
./build_android_arm64_v8a.sh $PATH_TO_NDK

+ '[' -z /home/.../Android/Sdk/ndk/27.2.12479018 ']'
+ CMAKE_BUILD_TYPE=Release
+ PATH_TO_NDK=/home/.../Android/Sdk/ndk/27.2.12479018
+ mkdir -p build
+ cd build
+ cmake -DCMAKE_TOOLCHAIN_FILE=/home/.../Android/Sdk/ndk/27.2.12479018/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=35 -DANDROID_STL=c++_shared -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF -DCMAKE_BUILD_TYPE=Release ..
-- Android: Targeting API '35' with architecture 'arm64', ABI 'arm64-v8a', and processor 'aarch64'
-- Android: Selected unified Clang toolchain
-- The C compiler identification is Clang 18.0.3
-- The CXX compiler identification is Clang 18.0.3
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /home/.../Android/Sdk/ndk/27.2.12479018/toolchains/llvm/prebuilt/linux-x86_64/bin/clang - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /home/.../Android/Sdk/ndk/27.2.12479018/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.9s)
-- Generating done (0.0s)
-- Build files have been written to: /mnt/tmpfs/library-android/build
+ cmake --build .
[ 50%] Building CXX object CMakeFiles/library.dir/library.cpp.o
[100%] Linking CXX shared library liblibrary.so
[100%] Built target library
+ cmake --install .
-- Install configuration: "Release"
-- Installing: /mnt/tmpfs/library-android/build/bin/liblibrary.so

I also cloned my branch with the patch in rust-lang/libc and built it on my phone in Termux.

cargo new app
cd app
cargo add libc --git https://github.com/StackOverflowExcept1on/libc.git --branch fix-android

src/main.rs

fn main() {
    const _: () = {
        assert!(std::mem::offset_of!(libc::ucontext_t, uc_flags) == 0);
        assert!(std::mem::offset_of!(libc::ucontext_t, uc_link) == 8);
        assert!(std::mem::offset_of!(libc::ucontext_t, uc_stack) == 16);
        assert!(std::mem::offset_of!(libc::ucontext_t, uc_sigmask__c_anonymous_union) == 40);
        assert!(std::mem::offset_of!(libc::ucontext_t, uc_mcontext) == 176);

        assert!(std::mem::size_of::<libc::ucontext_t>() == 4560);
    };
}
rustc --version --verbose
rustc 1.96.0 (ac68faa20 2026-05-25) (built from a source tarball)
binary: rustc
commit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96
commit-date: 2026-05-25
host: aarch64-linux-android
release: 1.96.0
LLVM version: 21.1.8
cargo build --release
   Compiling libc v1.0.0-alpha.3 (https://github.com/StackOverflowExcept1on/libc.git?branch=fix-android#f674b086)
   Compiling app v0.1.0 (/data/data/com.termux/files/home/app)
    Finished `release` profile [optimized] target(s) in 2.41s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

libc::ucontext_t does not match aarch64-linux-android NDK.

4 participants